﻿#region Using References

using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Beaker.OpenCube.Game.Blocks;
using Beaker.OpenCube.Game.Converters;
using Beaker.OpenCube.Mapping;

#endregion

namespace Beaker.OpenCube.Game
{
	public class Chunk
		: ObservableExpandoObject
	{
		private Coordinates2<int> _position;

		/// <summary>
		/// Gets or sets the position of the chunk.
		/// </summary>
		/// <value>
		/// The position of the chunk.
		/// </value>
		[Mapping( Names = new[] { "xPos", "zPos" }, Converter = typeof( Coordinates2Converter<int> ) )]
		public Coordinates2<int> Position
		{
			get { return _position; }
			set { SetValue( ref _position, value ); }
		}

		private long _lastUpdate;

		/// <summary>
		/// Gets or sets the tick when this chunk was last updated
		/// </summary>
		public long LastUpdate
		{
			get
			{
				Contract.Ensures( Contract.Result<long>() > 0 );
				return _lastUpdate;
			}
			set
			{
				Contract.Requires( value >= 0 );
				Contract.Ensures( _lastUpdate == value );
				SetValue( ref _lastUpdate, value );
			}
		}

		private bool _isTerrainPopulated;

		/// <summary>
		/// Gets or sets if this chunk has been populated
		/// </summary>
		/// <remarks>
		///  If set to false then Minecraft will regenerate these features in the blocks that already exist.
		/// </remarks>
		[Mapping( Name = "TerrainPopulated" )]
		public bool IsTerrainPopulated
		{
			get { return _isTerrainPopulated; }
			set
			{
				Contract.Ensures( _isTerrainPopulated == value );
				SetValue( ref _isTerrainPopulated, value );
			}
		}

		private readonly BiomeType[,] _biomes = new BiomeType[16, 16];

		/// <summary>
		/// Gets the Biome data. One value for every vertical column in the chunk.
		/// </summary>
		[Mapping( IsOptional = true )]
		public BiomeType[,] Biomes
		{
			get
			{
				Contract.Ensures( Contract.Result<BiomeType[,]>() != null );
				return _biomes;
			}
		}

		private readonly int[,] _heightMap = new int[16, 16];

		/// <summary>
		/// Gets the height map data.  [z,x]
		/// </summary>
		/// <remarks>
		/// Each byte records the lowest level in each column where the light from the sky is at full strength. 
		/// Speeds computing of the SkyLight.
		/// Note: This array's indexes are ordered ZX whereas the other array indexes are ordered XZ or YZX.
		/// </remarks>
		public int[,] HeightMap
		{
			get
			{
				Contract.Ensures( Contract.Result<int[,]>() != null );
				return _heightMap;
			}
		}

		private readonly List<ChunkSection> _sections = new List<ChunkSection>();


		/// <summary>
		/// Gest the list holding the sections
		/// </summary>
		public List<ChunkSection> Sections
		{
			get
			{
				Contract.Ensures( Contract.Result<List<ChunkSection>>() != null );
				return _sections;
			}
		}

		/// <summary>
		/// Gets the block information of the given chunk-section coordinates
		/// </summary>
		/// <param name="y">Y - Up, Down</param>
		/// <param name="z">Z - South, North</param>
		/// <param name="x">X - East, West</param>
		/// <returns></returns>
		[Mapping( Skip = true )]
		public WorldBlock this[byte y, byte z, byte x]
		{
			get
			{
				return BlockFactory.Create( this, y, z, x );
			}
		}

		/// <summary>
		/// Gets the most top block at the given position that is not Air.
		/// </summary>
		/// <returns>The most top block that is not Air.</returns>
		public WorldBlock GetTopBlock( byte x, byte z )
		{
			// we'll use the height map to get the top sun-lit item
			int y = HeightMap[z, x];

			// no block found
			if( y <= 0 )
				return null;

			// block below last sunlit is actual block
			y--;
			return BlockFactory.Create( this, (byte)y, z, x );
		}
	}
}
